package ffmpeg

/*
#cgo LDFLAGS: -lavformat -lavcodec -lswresample -lavutil -lz -lm

#include <stdlib.h>

#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
*/
import "C"

import (
	"bytes"
	"errors"
	"fmt"
	"log"
	"unsafe"
)

type Input struct {
	url     string
	fmtCtx  *C.AVFormatContext
	Streams []*AVStream
}

func Open(url string) (*Input, error) {
	csUrl := C.CString(url)
	defer C.free(unsafe.Pointer(csUrl))
	var fmtCtx *C.AVFormatContext
	if retCode := C.avformat_open_input(&fmtCtx, csUrl, nil, nil); retCode < 0 {
		return nil, errors.New(fmt.Sprintf("Call to avformat_open_input(%s) failed: %d.", url, retCode))
	}
	if retCode := C.avformat_find_stream_info(fmtCtx, nil); retCode < 0 {
		C.avformat_close_input(&fmtCtx)
		return nil, errors.New(fmt.Sprintf("Call to avformat_find_stream_info(%s) failed: %d.", url, retCode))
	}

	streams := getStreams(fmtCtx)
	for _, stream := range streams {
		if stream.codec.codec_id == C.AV_CODEC_ID_PROBE {
			log.Printf("Failed to probe codec for input stream %d\n", stream.index)
			continue
		}
		codec := C.avcodec_find_decoder(stream.codec.codec_id)
		if codec == nil {
			log.Printf("Unsupported codec with id %d for input stream %d\n", stream.codec.codec_id, stream.index)
			continue
		}
		if retCode := C.avcodec_open2(stream.codec, codec, nil); retCode < 0 {
			log.Printf("Could not open codec for input stream %d: %d\n", stream.index, retCode)
			continue
		}
	}
	return &Input{url, fmtCtx, streams}, nil
}

func (f *Input) Close() error {
	if f == nil || f.fmtCtx == nil {
		return errors.New("File never opened!")
	}
	for _, stream := range f.Streams {
		if stream.codec.codec_id == C.AV_CODEC_ID_NONE {
			continue
		}
		if retCode := C.avcodec_close(stream.codec); retCode < 0 {
			log.Printf("Call to avcodec_close for stream %d failed: %d\n", stream.index, retCode)
		}
	}
	C.avformat_close_input(&f.fmtCtx)
	return nil
}

func getDurationStr(duration C.int64_t) string {
	if duration < 0 {
		return "N/A"
	}
	return fmt.Sprintf("%d", int64(duration))
}

func (f *Input) DebugString() string {
	return fmt.Sprintf("start_time:%d duration:%s bit_rate:%d",
		int64(f.fmtCtx.start_time),
		getDurationStr(f.fmtCtx.duration),
		int64(f.fmtCtx.bit_rate))
}

func (s *AVStream) DebugString() string {
	var buffer bytes.Buffer
	buffer.WriteString(fmt.Sprintf(
		"Stream %d: id:%d start_time:%d duration:%s nb_frames:%d\n",
		int32(s.index), int32(s.id), int64(s.start_time), getDurationStr(s.duration), int64(s.nb_frames)))
	codecCtx := s.codec
	if codecCtx != nil {
		buffer.WriteString(fmt.Sprintf("\tcodec: %s", s.MediaType()))

		if codecCtx.codec_type == C.AVMEDIA_TYPE_VIDEO {
			buffer.WriteString(fmt.Sprintf(
				" width:%d height:%d", int(codecCtx.width), int(codecCtx.height)))
		}
		codec := codecCtx.codec
		if codec != nil {
			if codec.long_name != nil {
				buffer.WriteString(fmt.Sprintf(
					" long_name:%s", C.GoString(codec.long_name)))
			}
			if codec.name != nil {
				buffer.WriteString(fmt.Sprintf(
					" name:%s", C.GoString(codec.name)))
			}
		}
	}
	return buffer.String()
}
